/*

 * C++ implementation of the ICE encryption algorithm.

 *

 * Written by Matthew Kwan - July 1996

 * 

 * Error Handling Was Written by Jet Wu , NCR Taiwan , 2002

 * Porting By Charles Chu, NCR Taiwan, 2002

 */



#include "../include/IceKey.h"

#include <string.h>

/* The S-boxes */

static unsigned long ice_sbox[4][1024];

static int ice_sboxes_initialised = 0;



/* Modulo values for the S-boxes */

const int IceKey::ice_smod[4][4] = {

   {333, 313, 505, 369},

   {379, 375, 319, 391},

   {361, 445, 451, 397},

   {397, 425, 395, 505}};



/* XOR values for the S-boxes */

const int IceKey::ice_sxor[4][4] = {

   {0x83, 0x85, 0x9b, 0xcd},

   {0xcc, 0xa7, 0xad, 0x41},

   {0x4b, 0x2e, 0xd4, 0x33},

   {0xea, 0xcb, 0x2e, 0x04}};



/* Permutation values for the P-box */

const unsigned long IceKey::ice_pbox[32] = {

   0x00000001, 0x00000080, 0x00000400, 0x00002000,

   0x00080000, 0x00200000, 0x01000000, 0x40000000,

   0x00000008, 0x00000020, 0x00000100, 0x00004000,

   0x00010000, 0x00800000, 0x04000000, 0x20000000,

   0x00000004, 0x00000010, 0x00000200, 0x00008000,

   0x00020000, 0x00400000, 0x08000000, 0x10000000,

   0x00000002, 0x00000040, 0x00000800, 0x00001000,

   0x00040000, 0x00100000, 0x02000000, 0x80000000};



/* The key rotation schedule */

const int IceKey::ice_keyrot[16] = {

   0, 1, 2, 3, 2, 1, 3, 0,

   1, 3, 2, 0, 3, 1, 0, 2};





/*

 * 8-bit Galois Field multiplication of a by b, modulo m.

 * Just like arithmetic multiplication, except that additions and

 * subtractions are replaced by XOR.

 */

unsigned int IceKey::gf_mult(unsigned int a, unsigned int b, unsigned int m)

{

   unsigned int res = 0;



   while (b) {

      if (b & 1)

         res ^= a;



      a <<= 1;

      b >>= 1;



      if (a >= 256)

         a ^= m;

   }



   return (res);

}





/*

 * Galois Field exponentiation.

 * Raise the base to the power of 7, modulo m.

 */

unsigned long IceKey::gf_exp7(unsigned int b, unsigned int m)

{

   unsigned int x;



   if (b == 0)

      return (0);



   x = gf_mult (b, b, m);

   x = gf_mult (b, x, m);

   x = gf_mult (x, x, m);



   return (gf_mult (b, x, m));

}





/*

 * Carry out the ICE 32-bit P-box permutation.

 */



unsigned long IceKey::ice_perm32(unsigned long x)

{

   unsigned long res = 0;

   const unsigned long *pbox = IceKey::ice_pbox;



   while (x) {

      if (x & 1)

         res |= *pbox;



      pbox++;

      x >>= 1;

   }



   return (res);

}







// Initialise the ICE S-boxes.

// This only has to be done once.

void IceKey::ice_sboxes_init(void)

{

   int i;



   for (i=0; i<1024; i++) {

      int col = (i >> 1) & 0xff;

      int row = (i & 0x1) | ((i & 0x200) >> 8);

      unsigned long x;



      x = gf_exp7 (col ^ IceKey::ice_sxor[0][row], IceKey::ice_smod[0][row]) << 24;

      ice_sbox[0][i] = ice_perm32 (x);



      x = gf_exp7 (col ^ IceKey::ice_sxor[1][row], IceKey::ice_smod[1][row]) << 16;

      ice_sbox[1][i] = ice_perm32 (x);



      x = gf_exp7 (col ^ IceKey::ice_sxor[2][row], IceKey::ice_smod[2][row]) << 8;

      ice_sbox[2][i] = ice_perm32 (x);



      x = gf_exp7 (col ^ IceKey::ice_sxor[3][row], IceKey::ice_smod[3][row]);

      ice_sbox[3][i] = ice_perm32 (x);

   }

}





// Create a new ICE key.

IceKey::IceKey(int level)

{

   if (!ice_sboxes_initialised) {

      ice_sboxes_init ();

      ice_sboxes_initialised = 1;

   }



   if (level < 1) {

      _size = 1;

      _rounds = 8;

   } else {

      _size = level;

      _rounds = level * 16;

   }



   _keysched = new IceSubkey[_rounds];

}



// Destroy an ICE key.

IceKey::~IceKey ()

{

   int i, j;



   for (i=0; i<_rounds; i++)

      for (j=0; j<3; j++)

         _keysched[i].val[j] = 0;



   _rounds = _size = 0;



   delete[] _keysched;

}





// The single round ICE f function.

unsigned long IceKey::ice_f(unsigned long p, const IceSubkey *sk)

{

   unsigned long tl, tr; /* Expanded 40-bit values */

   unsigned long al, ar; /* Salted expanded 40-bit values */



   /* Left half expansion */

   tl = ((p >> 16) & 0x3ff) | (((p >> 14) | (p << 18)) & 0xffc00);



   /* Right half expansion */

   tr = (p & 0x3ff) | ((p << 2) & 0xffc00);



   /* Perform the salt permutation */

   // al = (tr & sk->val[2]) | (tl & ~sk->val[2]);

   // ar = (tl & sk->val[2]) | (tr & ~sk->val[2]);

   al = sk->val[2] & (tl ^ tr);

   ar = al ^ tr;

   al ^= tl;



   al ^= sk->val[0];		/* XOR with the subkey */

   ar ^= sk->val[1];



   /* S-box lookup and permutation */

   return (ice_sbox[0][al >> 10] | ice_sbox[1][al & 0x3ff]

           | ice_sbox[2][ar >> 10] | ice_sbox[3][ar & 0x3ff]);

}



// Encrypt a block of 8 bytes of data with the given ICE key.

void IceKey::encrypt(const unsigned char *ptext, unsigned char	*ctext)

{

   int i;

   unsigned long l, r;



   l = (((unsigned long) ptext[0]) << 24)

        | (((unsigned long) ptext[1]) << 16)

        | (((unsigned long) ptext[2]) << 8) | ptext[3];



   r = (((unsigned long) ptext[4]) << 24)

        | (((unsigned long) ptext[5]) << 16)

        | (((unsigned long) ptext[6]) << 8) | ptext[7];



   for (i = 0; i < _rounds; i += 2) {

      l ^= ice_f (r, &_keysched[i]);

      r ^= ice_f (l, &_keysched[i + 1]);

   }



   for (i = 0; i < 4; i++) {

      ctext[3 - i] = (unsigned char)(r & 0xff);

      ctext[7 - i] = (unsigned char)(l & 0xff);



      r >>= 8;

      l >>= 8;

   }

}



// Decrypt a block of 8 bytes of data with the given ICE key.

void IceKey::decrypt(const unsigned char *ctext, unsigned char *ptext)

{

   int		i;

   unsigned long	l, r;



   l = (((unsigned long) ctext[0]) << 24)

        | (((unsigned long) ctext[1]) << 16)

        | (((unsigned long) ctext[2]) << 8) | ctext[3];



   r = (((unsigned long) ctext[4]) << 24)

        | (((unsigned long) ctext[5]) << 16)

        | (((unsigned long) ctext[6]) << 8) | ctext[7];



   for (i = _rounds - 1; i > 0; i -= 2) {

      l ^= ice_f (r, &_keysched[i]);

      r ^= ice_f (l, &_keysched[i - 1]);

   }



   for (i = 0; i < 4; i++) {

      ptext[3 - i] = (unsigned char)(r & 0xff);

      ptext[7 - i] = (unsigned char)(l & 0xff);



      r >>= 8;

      l >>= 8;

   }

}



/* EnCode the data */

void IceKey::encode(unsigned char* data, unsigned char* k, char* output)

{

   int i;

   unsigned char buf[8];

   unsigned char enbuf[9];

   unsigned char key[64];

   int len = strlen((const char*)k);



   memset((void*)key, 0, sizeof(key));

   

   if (len > 64)

      strncpy((char*)key, (char*)k, 64);

   else

      strncpy((char*)key, (char*)k, len);



   this->set(key);

   

   len = strlen((const char*)data);

   int n = 0;

   int p = 0;

   while (1) {

      for (i=0; i<8; i++) {

         if ((n+i)>=len)

            buf[i] = 0;

         else

            buf[i] = data[n+i];

      }



      encrypt(buf, enbuf);

      n += 8;



      for (i=0; i<8; i++) {

         sprintf(&output[p], "%02x", enbuf[i]);

         p += 2;

      }



      if (n >= len)

         break;

   }

}



void IceKey::decode(unsigned char* data, unsigned char* k, char* output)

{

   unsigned char key[64];

   int len = strlen((const char*)k);

   int i,j;



   memset((void*)key, 0, sizeof(key));

   

   if (len > 64)

      strncpy((char*)key, (char*)k, 64);

   else

      strncpy((char*)key, (char*)k, len);



   this->set(key);



   len = strlen((const char*)data);

    

   unsigned char buf[9];

   unsigned char debuf[9];

   unsigned char ch=(char)NULL;

   unsigned char ch1=(char)NULL;

   unsigned char ch2=(char)NULL;



   int n = 0;

   int p = 0;

   while (1) {

      for (i=0; i<9; i++)

         buf[i] = '\0';



      for (i=0, j=0; i<8; i++) {

         ch1 = data[n+j++];

         ch2 = data[n+j++];



         if (ch1 >= '0' && ch1 <= '9') {

            ch = (ch1 - '0') << 4;

         } else if (ch1 >= 'a' && ch1 <= 'f') {

            ch = (ch1 - 'a' + 10) << 4;

         }



         if (ch2 >= '0' && ch2 <= '9') {

            ch += (ch2 - '0') ;

         } else if (ch2 >= 'a' && ch2 <= 'f') {

            ch += (ch2 - 'a' + 10);

         }



         buf[i] = (char)ch;

     }

 

     for (i=0; i<9; i++)

        debuf[i] = '\0';



     decrypt(buf, debuf);



     sprintf(&output[p], "%s", debuf);

     p += strlen((char*)debuf);



     n += 16;

     if (n >= len)

        break;

   }

}



// Set 8 rounds [n, n+7] of the key schedule of an ICE key.

void IceKey::scheduleBuild(unsigned short *kb, int n, const int *keyrot)

{

   int i;



   for (i=0; i<8; i++) {

      int j;

      int kr = keyrot[i];

      IceSubkey	*isk = &_keysched[n + i];



      for (j=0; j<3; j++)

         isk->val[j] = 0;



      for (j=0; j<15; j++) {

         int k;

         unsigned long *curr_sk = &isk->val[j % 3];



         for (k=0; k<4; k++) {

            unsigned short *curr_kb = &kb[(kr + k) & 3];

            int bit = *curr_kb & 1;



            *curr_sk = (*curr_sk << 1) | bit;

            *curr_kb = (*curr_kb >> 1) | ((bit ^ 1) << 15);

         }

      }

   }

}



// Set the key schedule of an ICE key.

void IceKey::set(const unsigned char *key)

{

   int i;



   if (_rounds == 8) {

      unsigned short kb[4];



      for (i=0; i<4; i++)

         kb[3 - i] = (key[i*2] << 8) | key[i*2 + 1];



      scheduleBuild (kb, 0, ice_keyrot);

      return;

   }



   for (i=0; i<_size; i++) {

      int j;

      unsigned short kb[4];



      for (j=0; j<4; j++)

         kb[3 - j] = (key[i*8 + j*2] << 8) | key[i*8 + j*2 + 1];



      scheduleBuild (kb, i*8, ice_keyrot);

      scheduleBuild (kb, _rounds - 8 - i*8, &ice_keyrot[8]);

   }

}



// Return the key size, in bytes.

int IceKey::keySize ()

{

   return (_size * 8);

}



// Return the block size, in bytes.

int IceKey::blockSize ()

{

   return (8);

}





